package com.ruoyi.flow.service.impl;

import com.ruoyi.audit.domain.vo.HandleAuditParam;
import com.ruoyi.audit.domain.vo.HistoryTask;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.flow.domain.BusBpmnInfo;
import com.ruoyi.flow.service.IProcessService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

@Service
public class ProcessServiceImpl implements IProcessService {
    public static final String ZIP_FILE_NAME = "zip";
    public static final List<String> ALLOW_EXT = Arrays.asList(ZIP_FILE_NAME, "bpmn", "xml");
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;

    /**
     * 部署流程定义
     *
     * @param bpmnFileName   流程文件名称
     * @param bpmnFileStream 流程文件输入流
     * @return
     */
    @Override
    public BusBpmnInfo deploy(String bpmnFileName, InputStream bpmnFileStream) {
        String suffix = FileUtils.getFilesSuffix(bpmnFileName);
        Assert.state(ALLOW_EXT.contains(suffix), "文件上传格式有误，仅支持bpmn/zip格式的文件");
        DeploymentBuilder deployment = repositoryService.createDeployment();
        if (ZIP_FILE_NAME.equals(suffix)) {
            deployment.addZipInputStream(new ZipInputStream(bpmnFileStream));
        } else {
            deployment.addInputStream(bpmnFileName, bpmnFileStream);
        }
        BusBpmnInfo busBpmnInfo = new BusBpmnInfo();
        Deployment deploy = deployment.deploy();
        try {
            bpmnFileStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        busBpmnInfo.setDepolyTime(deploy.getDeploymentTime());
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        Assert.notNull(processDefinition, "部署资源异常");
        busBpmnInfo.setBpmnLabel(processDefinition.getName());
        busBpmnInfo.setProcessDefinitionKey(processDefinition.getKey());
        busBpmnInfo.setVersion((long) processDefinition.getVersion());
        return busBpmnInfo;
    }

    /**
     * 获取文件输入流
     *
     * @param processDefinitionKey
     * @param version
     * @param type
     * @return
     */
    @Override
    public InputStream getResourceAsStream(String processDefinitionKey, Long version, String type) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .processDefinitionVersion(version.intValue())
                .singleResult();
        if (type.equals("xml")) {
            return repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
        }

        if (!StringUtils.isEmpty(processDefinition.getDiagramResourceName())) {
            return repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getDiagramResourceName());
        }
        ProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        return processDiagramGenerator.generateDiagram(bpmnModel, processDiagramGenerator.getDefaultActivityFontName(), processDiagramGenerator.getDefaultLabelFontName()
                , processDiagramGenerator.getDefaultAnnotationFontName());
    }

    /**
     * 撤销流程定义
     *
     * @param processDefinitionKey
     * @param version
     */
    @Override
    public void deleteDefinition(String processDefinitionKey, Long version) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .processDefinitionVersion(version.intValue())
                .singleResult();
        repositoryService.deleteDeployment(processDefinition.getDeploymentId());
    }

    /**
     * 获取模型对象
     */
    @Override
    public BpmnModel getBpmnModel(String definitionKey, Long version) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(definitionKey)
                .processDefinitionVersion(version.intValue())
                .singleResult();
        return repositoryService.getBpmnModel(processDefinition.getId());
    }

    /**
     * 开始流程实例
     *
     * @param processDefinitionKey 流程定义key
     * @param businessKey          业务标识
     * @param variables            流程变量
     * @return
     */
    @Override
    public String startProcessInstance(String processDefinitionKey, String businessKey, Map<String, Object> variables) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
        return processInstance.getId();
    }

    @Override
    public void deleteProcessInstance(String instanceId, String reason) {
        runtimeService.deleteProcessInstance(instanceId, reason);
    }

    /**
     * 查询业务标识列表
     *
     * @param processDefinitionKey 流程定义key
     * @param userId               用户id
     * @return
     */
    @Override
    public List<String> selectBusinessKeyList(String processDefinitionKey, Long userId) {
        List<Task> list = null;
        if (SecurityUtils.isAdmin(userId)) {
            list = taskService.createTaskQuery()
                    .processDefinitionKey(processDefinitionKey)
                    .list();
        } else {
            list = taskService.createTaskQuery()
                    .processDefinitionKey(processDefinitionKey)
                    .taskAssignee(userId + "")
                    .list();
        }
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        Set<String> instanceIdList = new HashSet<>();
        for (Task task : list) {
            instanceIdList.add(task.getProcessInstanceId());
        }
        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
                .processInstanceIds(instanceIdList)
                .list();
        List<String> businessKey = new ArrayList<>();
        for (ProcessInstance processInstance : processInstances) {
            businessKey.add(processInstance.getBusinessKey());
        }
        return businessKey;

    }

    /**
     * 完成任务，查询有没有下一个节点
     *
     * @param instanceId       流程实例id
     * @param userId           用户id，审核人
     * @param handleAuditParam 接受的参数
     * @return
     */
    @Override
    public boolean handleTaskByInstanceIdAndAssignee(String instanceId, Long userId, HandleAuditParam handleAuditParam) {
        Assert.state(!SecurityUtils.isAdmin(userId), "管理员不能审批");
        Task task = taskService.createTaskQuery()
                .processInstanceId(instanceId)
                .taskAssignee(userId + "")
                .singleResult();
        Assert.notNull(task, "处理失败，任务不存在");
        Map<String, Object> variables = new HashMap<>();
        variables.put("shopOwnerAudit", handleAuditParam.isPassed());
        taskService.addComment(task.getId(), instanceId, SecurityUtils.getUsername() + "[" + (handleAuditParam.isPassed() ? "同意" : "拒绝") + "]" + handleAuditParam.getInfo());
        taskService.complete(task.getId(), variables);
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(instanceId)
                .singleResult();
        return processInstance == null;
    }

    /**
     * 查询业务流程标识
     *
     * @param busBpmnInfo
     * @param userId
     * @return
     */
    @Override
    public List<String> selectHistoryBusinessKeyList(BusBpmnInfo busBpmnInfo, Long userId) {
        List<HistoricTaskInstance> historyTask = null;
        if (SecurityUtils.isAdmin(userId)) {
            historyTask = historyService.createHistoricTaskInstanceQuery()
                    .processDefinitionKey(busBpmnInfo.getProcessDefinitionKey())
                    .finished()
                    .list();
        } else {
            historyTask = historyService.createHistoricTaskInstanceQuery()
                    .processDefinitionKey(busBpmnInfo.getProcessDefinitionKey())
                    .taskAssignee(userId + "")
                    .finished()
                    .list();
        }
        if (CollectionUtils.isEmpty(historyTask)) {
            return Collections.emptyList();
        }
        Set<String> processInstanceIds = historyTask.stream()
                .map(t -> t.getProcessInstanceId())
                .collect(Collectors.toSet());
        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
                .processInstanceIds(processInstanceIds)
                .list();
        return historicProcessInstances.stream().map(e -> e.getBusinessKey()).collect(Collectors.toList());
    }

    /**
     * 获取历史审核任务
     *
     * @param instanceId
     * @return
     */
    @Override
    public List<HistoryTask> selectHistoryTaskListByInstanceId(Long instanceId) {
        List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(instanceId + "")
                .list();
        if (CollectionUtils.isEmpty(taskInstances)) {
            return Collections.emptyList();
        }
        return taskInstances.stream()
                .map(e -> {
                    if (e.getEndTime() == null) {
                        HistoryTask historyTask = new HistoryTask();
                        historyTask.setDuration(null);
                        historyTask.setStartTime(e.getStartTime());
                        historyTask.setEndTime(null);
                        historyTask.setName(e.getName());
                        historyTask.setComment("正在审核中");
                        return historyTask;
                    }
                    HistoryTask historyTask = new HistoryTask();
                    historyTask.setDuration(DateUtils.timeDistance(e.getDurationInMillis()));
                    historyTask.setStartTime(e.getStartTime());
                    historyTask.setEndTime(e.getEndTime());
                    historyTask.setName(e.getName());
                    if (!StringUtils.isEmpty(e.getDeleteReason())) {
                        historyTask.setComment(e.getDeleteReason());
                    } else {
                        List<Comment> comments = taskService.getTaskComments(e.getId());
                        if (!CollectionUtils.isEmpty(comments)) {
                            historyTask.setComment(comments.get(0).getFullMessage());
                        }
                    }
                    return historyTask;
                })
                .collect(Collectors.toList());
    }

    /**
     * 获取文件输入流,处理中的图片
     *
     * @param instanceId 流程实例id
     * @return
     */
    @Override
    public InputStream getProcessingImageByInstanceId(String instanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(instanceId)
                .singleResult();
        Assert.notNull(historicProcessInstance, "参数错误，流程实例不存在");
        BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
        List<String> activeActivityIds=null;
        if(historicProcessInstance.getEndTime()==null){
            activeActivityIds = runtimeService.getActiveActivityIds(instanceId);
        }else {
            activeActivityIds = Collections.emptyList();
        }
        return new DefaultProcessDiagramGenerator()
                .generateDiagram(bpmnModel, activeActivityIds, Collections.emptyList(), "宋体", "宋体", "宋体");
    }
}
